﻿using System;
using System.Windows.Media;
using System.Windows;
using WickedFlame.Core.State;
using System.Windows.Media.Imaging;
using System.Collections.Generic;

namespace WickedFlame.Core.Shapes
{
    public class SCurveShape : VectorShape, IPolygonShape, IStateCapturable, ICopyable
    {
        public SCurveShape()
            : this(0, 0, 1, 0)
        {
        }

        public SCurveShape(Point start, Point end)
            : this(start.X, start.Y, end.X, end.Y)
        {
        }

        public SCurveShape(double x1, double y1, double x2, double y2)
			: base()
		{
            StartPoint = new Point(x1, y1);
            StartCurvePoint = new Point(x1, y1);
            EndCurvePoint = new Point(x2, y2);
            EndPoint = new Point(x2, y2);
        }

        public SCurveShape(string source)
        {
            _definingGeometry = (PathGeometry)PathGeometry.Parse(source);

            StartPoint = _definingGeometry.Figures[0].StartPoint;
            EndPoint = ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point3;
            StartCurvePoint = ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point1;
            EndCurvePoint = ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point2;
        }

        PathGeometry _definingGeometry;
        private PathGeometry DefiningGeometry
        {
            get
            {
                if (_definingGeometry == null)
                {
                    _definingGeometry = new PathGeometry();
                    _definingGeometry = new PathGeometry();

                    BezierSegment bezier = new BezierSegment(
                        StartCurvePoint/*point 1 of curve*/,
                        EndCurvePoint/*point 2 of curve*/,
                        EndPoint/*end of line*/,
                        true);

                    PathSegmentCollection segmentCollection = new PathSegmentCollection();
                    segmentCollection.Add(bezier);


                    PathFigure figure = new PathFigure(
                        StartPoint/*start of line*/,
                        segmentCollection,
                        false);

                    _definingGeometry.Figures.Add(figure);
                }

                _definingGeometry.Figures[0].StartPoint = StartPoint;
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point3 = EndPoint;
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point1 = StartCurvePoint;
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point2 = EndCurvePoint;

                return _definingGeometry;
            }
        }

		#region Properties

		public override int TrackerCount
		{
			get
			{
				return 4;
			}
		}

		public string GeometryString
        {
            get
            {
                return DefiningGeometry.ToString();
            }
        }

        Point _startPoint;
        public Point StartPoint
        {
            get
            {
                return _startPoint;
            }
            set
            {
                _startPoint = value;
                this.Invalidate();
            }
        }

        Point _endPoint;
        public Point EndPoint
        {
            get
            {
                return _endPoint;
            }
            set
            {
                _endPoint = value;
                this.Invalidate();
            }
        }

        Point _startCurvePoint;
        public Point StartCurvePoint
        {
            get
            {
                return _startCurvePoint;
            }
            set
            {
                _startCurvePoint = value;
                this.Invalidate();
            }
        }

        Point _endCurvePoint;
        public Point EndCurvePoint
        {
            get
            {
                return _endCurvePoint;
            }
            set
            {
                _endCurvePoint = value;
                this.Invalidate();
            }
        }

        public override Rect Bounds
        {
            get
            {
                return DefiningGeometry.Bounds;
            }
        }

		public int X1
		{
			get
			{
				return (int)StartPoint.X;
			}
			set
			{
				_startPoint.X = value;
				this.Invalidate();
			}
		}

		public int Y1
		{
			get
			{
				return (int)StartPoint.Y;
			}
			set
			{
				_startPoint.Y = value;
				this.Invalidate();
			}
		}

		public int X2
		{
			get
			{
				return (int)EndPoint.X;
			}
			set
			{
				_endPoint.X = value;
				this.Invalidate();
			}
		}

		public int Y2
		{
			get
			{
				return (int)EndPoint.Y;
			}
			set
			{
				_endPoint.Y = value;
				this.Invalidate();
			}
		}

		public int OffsetX1
		{
			get
			{
				return (int)StartCurvePoint.X;
			}
			set
			{
				_startCurvePoint.X = value;
				this.Invalidate();
			}
		}

		public int OffsetY1
		{
			get
			{
				return (int)StartCurvePoint.Y;
			}
			set
			{
				_startCurvePoint.Y = value;
				this.Invalidate();
			}
		}

		public int OffsetX2
		{
			get
			{
				return (int)EndCurvePoint.X;
			}
			set
			{
				_endCurvePoint.X = value;
				this.Invalidate();
			}
		}

		public int OffsetY2
		{
			get
			{
				return (int)EndCurvePoint.Y;
			}
			set
			{
				_endCurvePoint.Y = value;
				this.Invalidate();
			}
		}

		#endregion

		#region Implementation

		public override void Invalidate()
		{
			base.Invalidate();

			NotifyPropertyChanged("StartPoint");
			NotifyPropertyChanged("EndPoint");
			NotifyPropertyChanged("X1");
			NotifyPropertyChanged("Y1");
			NotifyPropertyChanged("X2");
			NotifyPropertyChanged("Y2");

			NotifyPropertyChanged("OffsetX1");
			NotifyPropertyChanged("OffsetY1");
			NotifyPropertyChanged("OffsetX2");
			NotifyPropertyChanged("OffsetY2");

			DrawingCanvas.DrawTrackers();
		}

		public override void Draw(DrawingContext drawingContext)
        {
            drawingContext.DrawGeometry(Fill,
                Pen /*new Pen(Stroke, StrokeThickness)*/,
                DefiningGeometry);
        }

        public override void DrawTracker(DrawingContext drawingContext, double scale)
        {
            Pen p = new Pen(Brushes.Blue, 0.5 / scale);
            drawingContext.DrawLine(p, StartPoint, StartCurvePoint);
            drawingContext.DrawLine(p, EndPoint, EndCurvePoint);

            p = new Pen(Brushes.Blue, 1 / scale);
            double r = 2.5 / scale;

            if (SelectedTracker != 1)
                drawingContext.DrawEllipse(Brushes.LightBlue, p, StartPoint, r, r);
            else
                drawingContext.DrawEllipse(Brushes.White, new Pen(Brushes.Black, 2 / scale), StartPoint, r, r);

            if (SelectedTracker != 2)
                drawingContext.DrawEllipse(Brushes.LightBlue, p, StartCurvePoint, r, r);
            else
                drawingContext.DrawEllipse(Brushes.White, new Pen(Brushes.Black, 2 / scale), StartCurvePoint, r, r);

            if (SelectedTracker != 3)
                drawingContext.DrawEllipse(Brushes.LightBlue, p, EndCurvePoint, r, r);
            else
                drawingContext.DrawEllipse(Brushes.White, new Pen(Brushes.Black, 2 / scale), EndCurvePoint, r, r);

            if (SelectedTracker != 4)
                drawingContext.DrawEllipse(Brushes.LightBlue, p, EndPoint, r, r);
            else
                drawingContext.DrawEllipse(Brushes.White, new Pen(Brushes.Black, 2 / scale), EndPoint, r, r);
        }

        public override void SelectTracker(int tracker)
        {
            System.Diagnostics.Debug.WriteLine("SCurve : " + tracker);
            
            //if (tracker == 1 || tracker == 4)
            //    base.SelectTracker(tracker);

            base.SelectTracker(tracker);
        }

        /// <summary>
        /// Get handle point by 1-based number
        /// </summary>
        /// <param name="trackerNo"></param>
        /// <returns></returns>
        public override Point GetTracker(int trackerNo)
        {
            if (trackerNo == 1)
                return StartPoint;
            else if (trackerNo == 2)
                return StartCurvePoint;
            else if (trackerNo == 3)
                return EndCurvePoint;
            else
                return EndPoint;
        }

        //public override int HitTestInt(Point point)
        //{
        //    if (IsSelected)
        //    {
        //        var points = new Dictionary<int, Point>();
        //        for (int i = 1; i <= TrackerCount; i++)
        //        {
        //            if (GetTrackerRectangle(i).Contains(point))
        //                points.Add(i, GetTracker(i));
        //                //return i;
        //        }

        //        if (points.Count >= 1)
        //        {
                    
        //        }
        //    }

        //    if (PointInObject(point))
        //        return 0;

        //    return -1;
        //}

        protected override bool PointInObject(Point point)
        {
            return _definingGeometry.StrokeContains(Pen /*new Pen(this.Stroke, this.StrokeThickness)*/, point);
        }

        public override bool IntersectsWith(Rect rectangle)
        {
            var rectgeo = new RectangleGeometry(rectangle, 0, 0);
            var det = _definingGeometry.StrokeContainsWithDetail(Pen /*new Pen(this.Stroke, this.StrokeThickness)*/, rectgeo);

            if (det == IntersectionDetail.FullyContains || det == IntersectionDetail.FullyInside || det == IntersectionDetail.Intersects)
                return true;

            return false;
        }

        public override System.Windows.Input.Cursor GetTrackerCursor(int trackerNo)
        {
            switch (trackerNo)
            {
                case 1:
                case 2:
                case 3:
                case 4:
                    return System.Windows.Input.Cursors.SizeAll;
                default:
                    return System.Windows.Input.Cursors.Arrow;
            }
        }

        public override void MoveTracker(Point point, int trackerNo)
        {
            if (trackerNo == 1)
                StartPoint = point;
            else if (trackerNo == 2)
                StartCurvePoint = point;
            else if (trackerNo == 3)
                EndCurvePoint = point;
            else
                EndPoint = point;

            this.Invalidate();

            CallChangedHandler();
        }

        public override void MoveTracker(int tracker, double deltaX, double deltaY)
        {
            if (tracker == 1)
                StartPoint = new Point(StartPoint.X + deltaX, StartPoint.Y + deltaY);
            else if (tracker == 2)
                StartCurvePoint = new Point(StartCurvePoint.X + deltaX, StartCurvePoint.Y + deltaY);
            else if (tracker == 3)
                EndCurvePoint = new Point(EndCurvePoint.X + deltaX, EndCurvePoint.Y + deltaY);
            else
                EndPoint = new Point(EndPoint.X + deltaX, EndPoint.Y + deltaY);

            this.Invalidate();

            CallChangedHandler();
        }

        public override void Move(double deltaX, double deltaY)
        {
            StartPoint = new Point(StartPoint.X + deltaX, StartPoint.Y + deltaY);
            StartCurvePoint = new Point(StartCurvePoint.X + deltaX, StartCurvePoint.Y + deltaY);
            EndCurvePoint = new Point(EndCurvePoint.X + deltaX, EndCurvePoint.Y + deltaY);
            EndPoint = new Point(EndPoint.X + deltaX, EndPoint.Y + deltaY);

            CallMovedHandler();
        }

#endregion

        #region IHistoryable Members

        public ShapeMemento CreateMementoState()
        {
            var memento = new SCurveMemento();
            memento.SaveProperties(this);
            return memento;
        }

        public void ResetState(ShapeMemento memento)
        {
            memento.ResetProperties(this);
        }

        #endregion

        #region IPoligonShape Members

        public void AddShapeToPoligon(PolygonShape poligon)
        {
            //if (Distance(poligon.EndPoint, EndPoint) < Distance(poligon.EndPoint, StartPoint))
            if (SelectedTracker > 0)
            {
                // flip curve if the selected tracker is at the end
                if (SelectedTracker == 4)
                {
                    Point p1 = StartPoint;
                    Point p2 = EndPoint;
                    Point p3 = StartCurvePoint;

                    EndCurvePoint = StartCurvePoint;
                    StartCurvePoint = p3;
                    StartPoint = p2;
                    EndPoint = p1;
                }
            }
            else
                throw new ShapeException("No Tracker is isSelected");

            //poligon.AddSegment(new BezierSegment(
            //    ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point1,
            //    ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point2,
            //    ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point3,
            //    ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).IsStroked),
            //    StartPoint);
            poligon.AddSegment(new BezierSegment(
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point1,
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point2,
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point3,
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).IsStroked),
                EndPoint);
        }

        public PolygonShape CreatePolygon()
        {
            // If tracker is selected it would mean that the shape will be combined with another
            // Flip shape so that the selected trackers will join
            if (SelectedTracker == 1)
            {
                Point p1 = StartPoint;
                Point p2 = EndPoint;
                Point p3 = EndCurvePoint;

                EndCurvePoint = StartCurvePoint;
                StartCurvePoint = p3;
                StartPoint = p2;
                EndPoint = p1;
            }
            var polygon = new PolygonShape(StartPoint);
            polygon.AddSegment(new BezierSegment(StartCurvePoint, EndCurvePoint, EndPoint, true), EndPoint);

            polygon.Stroke = this.Stroke;
            polygon.StrokeThickness = this.StrokeThickness;
            polygon.Fill = this.Fill;

            return polygon;
        }

        #endregion

        #region ICopyable Members

        //public VectorShape Copy(Rect rectangle)
        public VectorShape Copy()
        {
            //if (rectangle.Width > 0 && rectangle.Height > 0)
            //{
            //    BitmapImage image = ImageShape.ShapeToScreenImage(this);

            //    Rect intersection = new Rect(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
            //    intersection.Intersect(Bounds);

            //    Int32Rect rect = new Int32Rect(
            //        (int)(intersection.X),// - Rectangle.X),
            //        (int)(intersection.Y),// - Rectangle.Y),
            //        (int)intersection.Width, (
            //        int)intersection.Height);

            //    CroppedBitmap cb = new CroppedBitmap(image, rect);

            //    return new ImageShape(ImageShape.BitmapSourceToImage(cb), intersection.X, intersection.Y);
            //}
            //else
            //{
                var shape = new SCurveShape(StartPoint.X,StartPoint.Y,EndPoint.X,EndPoint.Y);
                shape.StartCurvePoint = this.StartCurvePoint;
                shape.EndCurvePoint = this.EndCurvePoint;
                shape.Fill = this.Fill;
                shape.Stroke = this.Stroke;
                shape.StrokeThickness = this.StrokeThickness;

                return shape;
            //}
        }

        #endregion
    }
}
